package com.example.tys.baseapplication.net_tcp.frame.future;



import com.example.tys.baseapplication.net_tcp.frame.packet.BasePacket;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

/**
 * 对外暴露
 * 1.唤醒阻塞 （uniqueId） <p>
 * 2.阻塞 （msg） 处理Future与Message之间的关系,以适合网络请求  <p>
 * Created by tys
 * on 2019/1/23.
 */

public class FutureManager<R extends BasePacket> {

    private static final String TAG = "FutureManager";
    public volatile Map<String, AbstractCompletionFuture<R>> FUTURES_MAP = new ConcurrentHashMap<>(8);
    private FutureFactory<R> FUTURE_FACTORY;

    public FutureManager() {
        FUTURE_FACTORY = DefaultFuture::new;
    }

    public R get(String uniqueId, int timeOutMillis) throws InterruptedException, TimeoutException, RejectedExecutionException {
        AbstractCompletionFuture<R> future = FUTURE_FACTORY.newFuture(uniqueId);
        if (put(future)) {
            try {
                return future.get(timeOutMillis, TimeUnit.MILLISECONDS);
            } catch (TimeoutException e) {
                remove(future.uniqueId);
                throw new TimeoutException(e.getMessage());
            } catch (InterruptedException e) {
                remove(future.uniqueId);
                throw new InterruptedException(e.getMessage());
            }
        } else {
            throw new RejectedExecutionException("failed add to map, rejected uniqueId " + future.uniqueId);
        }
    }

    public boolean signal(R result) {
        String uniqueId = result.getUniqueId();
        AbstractCompletionFuture<R> future = remove(uniqueId);
        if (future == null) {
            System.out.println(TAG + " nonexistence in the map:" + uniqueId);
            return false;
        } else {
            future.setResult(result);
            return true;
        }
    }

    private AbstractCompletionFuture<R> remove(String uniqueId) {
        return FUTURES_MAP.remove(uniqueId);
    }

    private boolean put(AbstractCompletionFuture<R> future) {
        if (FUTURES_MAP.size() >= 6) {
            return false;
        }
        FUTURES_MAP.put(future.uniqueId, future);
        return true;
    }


}
